use crate::*;
use anyhow::{bail, Result};
use common::{GlmMessage, GlmRequestParam, GlmResponse, ChatHistory};
use leptos::leptos_dom::ev::SubmitEvent;
use leptos::logging;
use leptos::*;
use reqwest::header::HeaderMap;
use serde::{Deserialize, Serialize};
use serde_json::*;
use web_sys::History;
use std::{collections::HashMap};
use tracing::{debug, error, info};
use comrak::{markdown_to_html, nodes::NodeWikiLink, Options};
use chrono::prelude::*;


#[derive(Debug, Deserialize, Serialize, Clone, Default)]
pub struct GlmChatHis{
    pub timestamp: String,
    pub ques: String,    
    pub answer: String,
}

#[component]
pub fn GlmChartPage() -> impl IntoView {
    let (msg, set_msg) = create_signal(String::new());    
    let his_init:Vec<GlmChatHis> = vec![];
    let (his, set_his) = create_signal(his_init );
    
    view! {
      <div align="center">
      <input type="text" size="50" placeholder="input your question!" id="inputbox" on:input=move |ev| set_msg.set(event_target_value(&ev)) />
          
        <button style="margin:10px" on:click=move |_| {
          let new_input = msg.get();
          let old_his = his.get();
          reset_input("inputbox","");

          let chatview = document().get_element_by_id("chatview").unwrap();          
          chatview.set_inner_html("<div>  <p class=\"wait-tips\">Waiting ... ...!</p> </div><hr>");

          spawn_local(async move {
            let resp = chat_with_glm_response(&new_input).await;
            if resp.len() > 0 {          
              let st = Local::now().format("%Y-%m-%d %H:%M:%S").to_string();
              let mut new_his = vec![GlmChatHis{ques: new_input, answer: resp.clone(), timestamp: st}]; 
              new_his.append(&mut old_his.clone());             
              set_his.set(new_his); 
            }

            let chatview = document().get_element_by_id("chatview").unwrap();
            chatview.set_inner_html("<div> </div>");            
            });
          
        }>提问</button>

        <button style="margin:1px" on:click=move |_| {
          let val = get_input_value("inputbox");           
          logging::log!("{:?}", val);
          if val.len() > 0 {
              let _r = window().alert_with_message( &val);
          }         
          
        }>Test</button>
      </div>
      <div id="chatview"> </div>     
      <Transition fallback =|| view! { <div>"Loading..."</div> } >
              {                           
                 move || {                
                  let hiss = his.get();
                  hiss.into_iter().map(|s| {
                              view! {
                                      <div class="chat-line" >
                                          <strong>Ask:</strong>
                                          <p>
                                          {s.ques}" [" { s.timestamp } "]"
                                          </p>
                                      </div>
                                      <div class="chat-line">
                                          <strong>Answer:</strong>
                                          <p>{s.answer}</p>
                                          <hr style= "border:1px dashed;" />
                                      </div>
                                  }
                              })
                              .collect::<Vec<_>>()       
                  }
            }
      </Transition>     
    }
}


#[component]
pub fn GlmChartPage2() -> impl IntoView {
    let (msg, set_msg) = create_signal(String::new());    
    let his_init:Vec<GlmChatHis> = vec![];
    let (his, set_his) = create_signal(his_init );
 
    view! {
      <div align="center">          
      <input type="text" size="50" placeholder="input your question!"  id="inputbox" on:input=move |ev| set_msg.set(event_target_value(&ev)) />          
        <button  style="margin:10px"   on:click=move |_| {
          let new_input = msg.get();
          let old_his = his.get();
          reset_input("inputbox","");           

          let chatview = document().get_element_by_id("chatview").unwrap();
          let oldview = chatview.inner_html();
          chatview.set_inner_html(&format!("<div>  <p class=\"wait-tips\">Waiting ... ...!</p> </div><hr>{}", oldview));
          
          spawn_local(async move {
              let resp = chat_with_glm_response(&new_input).await;
              if resp.len() > 0 {          
                  let st = Local::now().format("%Y-%m-%d %H:%M:%S").to_string();
                  let mut new_his = vec![GlmChatHis{ques: new_input.clone(), answer: resp.clone(), timestamp: st.clone()}]; 
                  new_his.append(&mut old_his.clone());             
                  set_his.set(new_his.clone()); 
                  
                  let mut options = Options::default();
                  options.extension.table = true;
                  let html_src = markdown_to_html(&resp, &options);

                  let chatview = document().get_element_by_id("chatview").unwrap();                  
                  let new_view = format!("<div><strong>Question：</strong>{}[{}]</div><div><strong>Answer：</strong>{}</div><hr style='border:1px dashed;' />", new_input, st, html_src );
                  chatview.set_inner_html(&format!("{}{}",new_view, oldview));
              }
            });
          
        }>提问</button>       
      </div>

      <div id="chatview"> </div>       
      
    }
}


#[component]
pub fn GlmChartPage3() -> impl IntoView {
    let (msg, set_msg) = create_signal(String::new());        
    let his_init:Vec<GlmChatHis> = vec![];
    let (his, set_his) = create_signal(his_init );
    let (quest, set_quest) = create_signal(String::new());    
    let answers = create_resource(move || quest.get() , |new_quest| async move { 
        if new_quest.len() > 0 { 
          (new_quest.clone(), chat_with_glm_response(&new_quest).await)       
        } 
        else { ("".to_string(), "".to_string()) } 
      } );

    view! {
      <div align="center">          
      <input type="text" size="50" placeholder="input your question!"  id="inputbox" on:input=move |ev| set_msg.set(event_target_value(&ev)) />          
        <button  style="margin:10px"   on:click=move |_| {
          let new_input = msg.get();
          if new_input.len() > 0 {
             // 此处更新了quest，必将触发 answers 重新执行更新！ 
             set_quest.set(new_input);           
          }          
        }>提问</button>       
      </div>

      <div id="chatview"> 
      <p>"这里演示了Sespense VS Transition异步过程中的不同表现："</p>
      <Transition fallback=move || view! { <p class="wait-tips">"Waiting with Transition ... ...!"</p> <hr/>  }>  
      <h2 class="title">"Transition"</h2>
      {
          move || {          
            match answers.get(){
            Some(ans) => {
              let new_input = quest.get();              
              if ans.0.eq(&new_input) && ans.0.len()> 0 {
                let old_his = his.get();
                let st = Local::now().format("%Y-%m-%d %H:%M:%S").to_string();                
                let mut new_his = vec![GlmChatHis{ques: new_input.clone(), answer: ans.1.clone(), timestamp: st.clone()}]; 
                new_his.append(&mut old_his.clone());             
                set_his.set(new_his.clone());
                reset_input("inputbox", "");
              }
              logging::log!("{:#?} is responsed", ans.0);
            },
            None => {
                // view! { <p class="wait-tips">"None ... ...!"</p> <hr/>  }
            }
          }
  
          let hiss = his.get();
          hiss.into_iter().map(|s| {
                      view! {
                              <div class="chat-line" >
                                  <strong>Ask:</strong>
                                  <p>
                                  {s.ques}" [" { s.timestamp } "]"
                                  </p>
                              </div>
                              <div class="chat-line">
                                  <strong>Answer:</strong>
                                  <p>{s.answer}</p>
                                  <hr style= "border:1px dashed;" />
                              </div>
                          }
                      })
                      .collect::<Vec<_>>()       
          }     
      
    }
      </Transition>

      <Suspense fallback=move || view! { <p class="wait-tips">"Waiting with Suspense ... ...!"</p> <hr/>  }>
      <h2 class="title">"Suspense"</h2>
      {move || {
          match answers.get(){
            Some(ans) => {
              logging::log!("{:#?} is responsed", ans.0);
              /* 
              // 这段逻辑也可以在这里执行，也可以放在Transition中执行。 效果都是一样的！！！

              let new_input = quest.get();              
              if ans.0.eq(&new_input) && ans.0.len()> 0 {
                let old_his = his.get();
                let st = Local::now().format("%Y-%m-%d %H:%M:%S").to_string();                
                let mut new_his = vec![GlmChatHis{ques: new_input.clone(), answer: ans.1.clone(), timestamp: st.clone()}]; 
                new_his.append(&mut old_his.clone());             
                set_his.set(new_his.clone());
                reset_input("inputbox", "");
              }
              */
            },
            None => {
               // view! { <p class="wait-tips">"None ... ...!"</p> <hr/>  }
            }
          }

          let hiss = his.get();
          hiss.into_iter().map(|s| {
                      view! {
                              <div class="chat-line" >
                                  <strong>Ask:</strong>
                                  <p>
                                  {s.ques}" [" { s.timestamp } "]"
                                  </p>
                              </div>
                              <div class="chat-line">
                                  <strong>Answer:</strong>
                                  <p>{s.answer}</p>
                                  <hr style= "border:1px dashed;" />
                              </div>
                          }
                      })
                      .collect_view()

        }
    }
    </Suspense>
  
    </div>       
      
    }
}


#[component]
pub fn GlmChartPage4() -> impl IntoView {
    let (msg, set_msg) = create_signal(String::new());        
    let his_init:Vec<ChatHistory> = vec![];
    let (his, set_his) = create_signal(his_init );

    // 读取历史记录
    spawn_local(async move {            
        let args = serde_wasm_bindgen::to_value(&GreetArgs { name: "" }).unwrap();
        let r = invoke("chat_chat", args).await; 
        let rs:Vec<ChatHistory> = serde_wasm_bindgen::from_value(r).unwrap();
        if rs.len() > 0{
           set_his.set(rs); 
        }      
    });
    
    view! {
      <div align="center">          
      <input type="text" size="50" placeholder="input your question!"  id="inputbox" on:input=move |ev| set_msg.set(event_target_value(&ev)) />          
      <button style="margin:10px" on:click=move |_| {
        let new_input = msg.get();       
        spawn_local(async move {
            if new_input.len() > 0 {          
              
              let chatview = document().get_element_by_id("chatview").unwrap();          
              chatview.set_inner_html("<div>  <p class=\"wait-tips\">Waiting ... ...!</p> </div><hr>");

              let args = serde_wasm_bindgen::to_value(&GreetArgs { name: &new_input }).unwrap();
              let r = invoke("chat_chat", args).await; 
              let rs:Vec<ChatHistory> = serde_wasm_bindgen::from_value(r).unwrap();
              if rs.len() > 0{
                set_his.set(rs); 
              }              
              chatview.set_inner_html(" ");
           }
           reset_input("inputbox","");         
        });
        
      }>提问</button>    
      </div>

      <div id="chatview"> </div>
      <div align="left">
      <Transition>  
      <h2 class="title">"Your chat list"</h2>
      {
          move || {  
          let hiss = his.get();
          hiss.into_iter().map(|s| {
                      view! {
                              <div class="chat-line" >
                                  <strong>Ask:</strong>
                                  <p>
                                  {s.question}" [" { s.timestamp } "]"
                                  </p>
                              </div>
                              <div class="chat-line">
                                  <strong>Answer:</strong>
                                  <p>{s.response}</p>
                                  <hr style= "border:1px dashed;" />
                              </div>
                          }
                      })
                      .collect::<Vec<_>>()       
          }     
      
    }
    </Transition>  
    </div>       
      
    }
}
/**
 * 聊天接口的包装，返回应答的内容（String）。
 * @param msg
 * @return
 */
pub async fn chat_with_glm_response(msg: &str) -> String {
  match chat_with_glm(msg).await{
    anyhow::Result::Ok(rs) =>      
      if rs.choices.len() > 0 {
        if rs.choices.len() > 1 {
            logging::log!("response msg is more than 1, but only use the first one now!");
        }
        let choice = rs.choices[0].clone();
        let content = choice.message.content;
        return content;
      }else{
        return "".to_string();
      },
    Err(er)=> {
      logging::log!("error {:?} ", er);
      return "".to_string();
    },
  }
}

/**
 * 重置指定ID的输入框（type=input）的值。
 * @param input_id
 * @param val
 * @spec: 这里提供了一种基于 js_sys::eval(js_code) ，执行js代码的方法或途径。
 */
fn reset_input(input_id: &str, val:&str) {    
  let js_src = format!("document.getElementById('{}').value ='{}'", input_id, val); 
  js_sys::eval(&js_src).ok();  
}
/**
 * 获取指定ID的输入框（type=input）的值。
 * @param input_id
 * @spec: 这里提供了一种基于 js_sys::eval(js_code) ，执行js代码的方法或途径。
 */
fn get_input_value(input_id: &str)->String {  
  let js_src = format!("document.getElementById('{}').value", input_id); 
  let s = js_sys::eval(&js_src).unwrap();  
  match s.as_string(){
    Some(s) => return s,
    None => return "".to_string(),
  }
}
/**
 * 简单调用GLM开方接口，采用glm-4-flash模型，进行对话。
 */
pub async fn chat_with_glm(msg: &str) -> anyhow::Result<GlmResponse> {
    if msg.is_empty(){
      bail!("msg is empty");
    }
    let client = reqwest::Client::new();
    let api = "https://open.bigmodel.cn/api/paas/v4/chat/completions";

    let mut headers = HeaderMap::new();
    headers.insert(
        "Authorization",
        "Bearer efee28671779b38ba107b5a8f89491cc.V2R8xEzrqo5FG8RT".parse()?,
    );
    headers.insert("Content-Type", "application/json; charset=utf-8".parse()?);

    let messages = vec![GlmMessage {
        role: "user".to_string(),
        content: msg.to_string(),
    }];

    // logging::log!("{:#?}", messages);

    let askmsg: GlmRequestParam = GlmRequestParam {
        model: "glm-4-flash".to_string(),
        messages,
        temperature: 0.9,
    };

    let r: GlmResponse = client
        .post(api)
        .headers(headers)
        .json(&askmsg)
        .send()
        .await?
        .json()
        .await?;
    // logging::log!("{:#?}", r);
    return Ok(r);
}
